﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using GameEngine.Model.Data;
using GameEngine.View.Components;
using MapEngine;
using System.IO;
using System.Windows.Media.Imaging;

namespace GameEngine.Clients.Silverlight.View.Components
{
    public partial class ItemUC : UserControl, IThingView<int, FrameworkElement>
    {
        public const char separator = ';';
        public const string ShapeTypeUploadedImage = "UploadedImage";
        public const string ShapeTypeProjectImage = "ProjectImage";
        public const string ShapeTypeGameResourceImage = "GameResourceImage";
        public const string ShapeTypeSprite = "Sprite";
        public const string ShapeTypeCube = "Cube";
        public const string ShapeTypePyramid = "Triangle";
        public const string ShapeTypeElipse = "Elipse";

        IDictionary<IViewStateVO,List<UIElement>> Elements;
        IDictionary<IViewStateVO, Storyboard> Animations;

        public enum ItemType { Normal, Isometric };
        
        public event MouseButtonEventHandler ItemMouseLeftButtonUp;
        public event MouseEventHandler ItemMouseMove;
        public event MouseEventHandler ItemMouseLeave;
        public event MouseEventHandler ItemMouseEnter;

        string tooltip = null;
        public string Tooltip{
            get { return tooltip; }
            set {
                tooltip = value;
                if (!string.IsNullOrEmpty(tooltip))
                {
                    ToolTipService.SetToolTip(LayoutRoot, new TextBlock()
                    {
                        Text = Tooltip
                    }
                        );
                }
            }
        }

        public ItemUC()
        {
            InitializeComponent();

            //Tooltip = tooltip;
                    /*                <ToolTipService.ToolTip>
            <StackPanel>
                <TextBlock><Run Text="Tooltip in a mouseover event"/></TextBlock>
            </StackPanel>
        </ToolTipService.ToolTip>*/
            
            Elements = new Dictionary<IViewStateVO, List<UIElement>>();
            Animations = new Dictionary<IViewStateVO, Storyboard>();
        }

        public void Initialize(IViewVO<int> viewVO)
        {
            foreach (ViewStateVO state in viewVO.Shapes.Keys)
            {
                foreach (IShapeVO shape in viewVO.Shapes[state].Values)
                {
                    IShapeTypeVO shapeType = SilverlightModelFacade.Instance.ShapeTypes[shape.TypeId];
                    switch (shapeType.Id.Value)
                    {
                        //case "LoadSilverlight" -> n pode permissoes(de fora)
                        case ShapeTypeUploadedImage://ok
                            try
                            {
                                BitmapImage imageSource = new BitmapImage();
                                imageSource.SetSource(((FileInfo)shape.TypeModifiers).OpenRead());
                                Image image = new Image() { Source = imageSource };
                                AddElement(state,
                                    image,
                                    shape.Position,
                                    shape.Rotation,
                                    shape.Scale);
                                if (imageSource.PixelWidth > Width)
                                    Width = imageSource.PixelWidth;
                                if (imageSource.PixelHeight > Height)
                                    Height = imageSource.PixelHeight;
                            }
                            catch (Exception)
                            {
                                MessageBox.Show("Error Loading File");
                            }
                            break;
                        case ShapeTypeProjectImage://ok png bmp
                            try
                            {
                                BitmapImage imageSource = new BitmapImage()
                                {
                                    UriSource = (Uri)shape.TypeModifiers
                                };
                                Image image = new Image() { Source = imageSource };
                                AddElement(state,
                                    image,
                                    shape.Position,
                                    shape.Rotation,
                                    shape.Scale);
                                if (imageSource.PixelWidth > Width)
                                    Width = imageSource.PixelWidth;
                                if (imageSource.PixelHeight > Height)
                                    Height = imageSource.PixelHeight;
                            }
                            catch (Exception)
                            {
                                MessageBox.Show("Error Loading File");
                            }
                            break;
                        case ShapeTypeGameResourceImage:
                            //try
                            {
                                string[] args = ((string)shape.TypeModifiers).Split(separator);
                                //Image image = SilverlightModelFacade.Instance.Engine.Game.Resources[args[0]] as Image;
                                Image image = Application.Current.Resources[args[0]] as Image;
                                AddElement(state,
                                    image,
                                    shape.Position,
                                    shape.Rotation,
                                    shape.Scale);
                                if (image.Width > Width)
                                    Width = image.Width;
                                if (image.Height > Height)
                                    Height = image.Height;
                            }
                            //catch (Exception e)
                            //{
                            //    MessageBox.Show(/*"Error Loading File."+*/e.Message);
                            //}
                            break;
                        case ShapeTypeSprite:
                            //try
                            {
                                string[] args = ((string)shape.TypeModifiers).Split(separator);
                                //Image image = SilverlightModelFacade.Instance.Engine.Game.Resources[args[0]] as Image;
                                Image image = new Image()
                                {
                                    Source = (Application.Current.Resources[args[0]] as Image).Source
                                };
                                int numberOfImages = int.Parse(args[1]);
                                float fps = float.Parse(args[2]);
                                image.Clip = new RectangleGeometry() { Rect = new Rect() };
                                List<Geometry> frames = new List<Geometry>();
                                for (int i = 0; i < numberOfImages; i++)
                                {
                                    Point p1 = new Point(int.Parse(args[3 + i * 4]), int.Parse(args[3 + i * 4 + 1]));
                                    Point p2 = new Point(int.Parse(args[3 + i * 4 + 2]), int.Parse(args[3 + i * 4 + 3]));
                                    frames.Add(new RectangleGeometry() { Rect = new Rect(p1, p2) });
                                }
                                //image.Clip = new RectangleGeometry(){Rect = new Rect()};
                                AddSprite(state,
                                    image,
                                    shape.Position,
                                    shape.Rotation,
                                    shape.Scale, fps, frames);
                                Width = frames[0].Bounds.Width;
                                Height = frames[0].Bounds.Height;
                            }
                            //catch (Exception e)
                            //{
                            //    MessageBox.Show("Error Loading File."+e.Message);
                            //}
                            break;
                        case ShapeTypeElipse:
                            {
                                string[] args = ((string)shape.TypeModifiers).Split(separator);
                                AddEllipse(state,
                                    new Point3d(new Point(double.Parse(args[0]), double.Parse(args[1])), float.Parse(args[2])),
                                    new Point(double.Parse(args[3]), double.Parse(args[4])),
                                    new Color() { R = byte.Parse(args[5]), G = byte.Parse(args[6]), B = byte.Parse(args[7]), A = byte.Parse(args[8]) },
                                    new ItemUC.StrokeHelper()
                                    {
                                        UseStroke = true,
                                        StrokeColor = Colors.Black,
                                        StrokeThickness = 1
                                    },
                                    true);
                                Width = double.Parse(args[0]) + double.Parse(args[3]);
                                Height = double.Parse(args[1]) + double.Parse(args[4]);
                            }
                            break;
                        case ShapeTypePyramid:
                            {
                                string[] args = ((string)shape.TypeModifiers).Split(separator);
                                AddPyramid(state,
                                    ItemUC.ItemType.Isometric,//TODO ir buscar a game
                                    new Point3d(new Point(double.Parse(args[0]), double.Parse(args[1])), float.Parse(args[2])),
                                    new Point3d(new Point(double.Parse(args[3]), double.Parse(args[4])), float.Parse(args[5])),
                                    new Point(double.Parse(args[6]),double.Parse(args[7])),
                                    new Color() { R = byte.Parse(args[8]), G = byte.Parse(args[9]), B = byte.Parse(args[10]), A = byte.Parse(args[11]) },
                                    new ItemUC.StrokeHelper()
                                    {
                                        UseStroke = true,
                                        StrokeColor = Colors.Black,
                                        StrokeThickness = 1
                                    },
                                    true);
                                Width = double.Parse(args[0]) + double.Parse(args[3]);
                                Height = double.Parse(args[5]);
                            }
                            break;
                        case ShapeTypeCube:
                        default:
                            {
                                string[] args = ((string)shape.TypeModifiers).Split(separator);
                                AddCube(state,
                                    ItemUC.ItemType.Isometric,//TODO ir buscar a game
                                    new Point3d(new Point(double.Parse(args[0]), double.Parse(args[1])), float.Parse(args[2])),
                                    new Point3d(new Point(double.Parse(args[3]), double.Parse(args[4])), float.Parse(args[5])),
                                    new Color() { R = byte.Parse(args[6]), G = byte.Parse(args[7]), B = byte.Parse(args[8]), A = byte.Parse(args[9]) },
                                    new ItemUC.StrokeHelper()
                                    {
                                        UseStroke = true,
                                        StrokeColor = Colors.Black,
                                        StrokeThickness = 1
                                    },
                                    true);
                                Width = double.Parse(args[0]) + double.Parse(args[3]);
                                Height = double.Parse(args[5]);
                            }
                            break;
                    }
                }
            }

            UpdateState(IViewVOConstants.DefaultState);

            /*return new FrameworkElementView(view);
         * 
         * */
        }

        IViewStateVO CurrentState = null;
        public void UpdateState(IViewStateVO state) {
            if(CurrentState != null)
                if (Animations.ContainsKey(CurrentState))
                    Animations[CurrentState].Stop();
            LayoutRoot.Children.Clear();

            CurrentState = state;
            double biggestHeightEl = 0;
            double biggestWidthEl = 0;
            if (Elements.ContainsKey(state))
            {
                foreach (UIElement el in Elements[state])
                {
                    LayoutRoot.Children.Add(el);
                    if (el.RenderSize.Height > biggestHeightEl)
                    {
                        biggestHeightEl = el.RenderSize.Height;
                    }
                    if (el.RenderSize.Width > biggestWidthEl)
                    {
                        biggestWidthEl = el.RenderSize.Width;
                    }
                }
            }
            if(Animations.ContainsKey(state))
                Animations[state].Begin();
            //Height = biggestHeightEl;
            //Width = biggestWidthEl;
        }

        public void AddElement(IViewStateVO state, FrameworkElement uiElement, Point3d position, Point3d rotation, Point3d scale)
        {
            TransformGroup tg = new TransformGroup();
            tg.Children.Add(new TranslateTransform() { X = position.X, Y = position.Y });
            tg.Children.Add(new ScaleTransform() { ScaleX = scale.X, ScaleY = scale.Y });
            if (rotation != null) {
                //tg.Children.Add(new RotateTransform() { Angle });
            }

            uiElement.RenderTransform = tg;
            uiElement.MouseLeftButtonUp += new MouseButtonEventHandler(shape_MouseLeftButtonUp);
            uiElement.MouseEnter += new MouseEventHandler(shape_MouseEnter);
            uiElement.MouseLeave += new MouseEventHandler(shape_MouseLeave);
            uiElement.MouseMove += new MouseEventHandler(shape_MouseMove);

            if(!Elements.ContainsKey(state))
                Elements.Add(state,new List<UIElement>());
            
            Elements[state].Add(uiElement);
            //LayoutRoot.Children.Add(uiElement);
        }
        
        public static String createSpriteModifiers(String resourceName, int numberOfImages, float fps, List<Rect> frames)
        {
            String ret = resourceName + separator + numberOfImages + separator + fps;
            foreach(Rect r in frames){
                ret += "" + separator + r.X + separator + r.Y + separator + r.Width + separator + r.Height;
            }
            return ret;
        }

        //int fps;
        //List<Geometry> frames;
        public void AddSprite(IViewStateVO state, FrameworkElement uiElement, Point3d position, Point3d rotation, Point3d scale, float fps, List<Geometry> frames)
        {
            //this.fps = fps;
            //this.frames = frames;
            AddElement(state, uiElement, new Point3d(new Point(position.X - frames[0].Bounds.Left, position.Y - frames[0].Bounds.Bottom), position.Z), rotation, scale);
            UpdateState(state);
            uiElement.Clip = frames[0];

            if (frames.Count > 1)
            {
                //LayoutRoot.Children.Add(uiElement);
                Duration duration = new Duration(TimeSpan.FromSeconds(frames.Count * 1 / fps));

                Storyboard sb = new Storyboard()
                {
                    Duration = duration,
                    RepeatBehavior = RepeatBehavior.Forever
                };

                ObjectAnimationUsingKeyFrames RectAnimation = new ObjectAnimationUsingKeyFrames() { Duration = duration };
                DoubleAnimationUsingKeyFrames XAnimation = new DoubleAnimationUsingKeyFrames() { Duration = duration };
                DoubleAnimationUsingKeyFrames YAnimation = new DoubleAnimationUsingKeyFrames() { Duration = duration };

                float lasttime = 0;
                foreach (Geometry g in frames)
                {
                    RectAnimation.KeyFrames.Add(new DiscreteObjectKeyFrame() { Value = g.Bounds, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(lasttime)) });
                    XAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame() { Value = position.X - g.Bounds.Left, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(lasttime)) });
                    YAnimation.KeyFrames.Add(new DiscreteDoubleKeyFrame() { Value = position.Y - g.Bounds.Bottom, KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromSeconds(lasttime)) });
                    lasttime = lasttime + 1 / fps;
                }

                sb.Children.Add(RectAnimation);
                sb.Children.Add(XAnimation);
                sb.Children.Add(YAnimation);

                Storyboard.SetTarget(RectAnimation, uiElement.Clip);
                Storyboard.SetTarget(XAnimation, ((TranslateTransform)((TransformGroup)uiElement.RenderTransform).Children[0]));
                Storyboard.SetTarget(YAnimation, ((TranslateTransform)((TransformGroup)uiElement.RenderTransform).Children[0]));

                Storyboard.SetTargetProperty(RectAnimation, new PropertyPath("(Bounds.Rect)"));
                Storyboard.SetTargetProperty(XAnimation, new PropertyPath("(X)"));
                Storyboard.SetTargetProperty(YAnimation, new PropertyPath("(Y)"));

                Animations.Add(state, sb);
                //sb.Begin();
            }
        }

        public static String createCubeModifiers(Point3d position, Point3d size, Color color)
        {
            return getPoint3d(position) + separator + getPoint3d(size) +separator + getColor(color);
        }

        public void AddCube(IViewStateVO state, ItemType type, Point3d position, Point3d size, Color color, StrokeHelper stroke, bool useMouse)
        {
            List<Point3d> vertices = new List<Point3d>();
            
            //topo
            vertices.Add(new Point3d(new Point(position.Position.X - size.Position.X / 2, position.Position.Y - size.Position.Y / 2), size.Height));
            vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y - size.Position.Y / 2), size.Height));
            vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), size.Height));
            vertices.Add(new Point3d(new Point(position.Position.X - size.Position.X / 2, position.Position.Y + size.Position.Y / 2), size.Height));

            if (size.Height != 0)
            {
                //frente
                vertices.Add(new Point3d(new Point(position.Position.X - size.Position.X / 2, position.Position.Y + size.Position.Y / 2), 0));
                vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), 0));
                vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), size.Height));
                vertices.Add(new Point3d(new Point(position.Position.X - size.Position.X / 2, position.Position.Y + size.Position.Y / 2), size.Height));

                //lado
                vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), 0));
                vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), size.Height));
                vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y - size.Position.Y / 2), size.Height));
                vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y - size.Position.Y / 2), 0));
            }

            List<Shape> Shapes = new List<Shape>();
            switch (type)
            {
                case ItemType.Normal:
                    Point p1 = new Point(vertices[0].Position.X, vertices[0].Position.Y);
                    Point p2 = new Point(vertices[1].Position.X, vertices[1].Position.Y);
                    Point p3 = new Point(vertices[2].Position.X, vertices[2].Position.Y);
                    Point p4 = new Point(vertices[3].Position.X, vertices[3].Position.Y);

                    Polygon t1 = new Polygon();
                    t1.Points.Add(p1);
                    t1.Points.Add(p2);
                    t1.Points.Add(p3);
                    t1.Points.Add(p4);

                    t1.Fill = new SolidColorBrush(color);
                    if (stroke.UseStroke) {
                        t1.Stroke = new SolidColorBrush(stroke.StrokeColor);
                        t1.StrokeThickness = stroke.StrokeThickness;
                    }
                    
                    Shapes.Add(t1);

                    break;
                case ItemType.Isometric:
                    Polygon square = new Polygon();
                    foreach (Point3d point in vertices)
                    {
                        square.Points.Add(IsometricMath.PointFrom3d(point));
                        if (square.Points.Count == 4)
                        {
                            square.Fill = new SolidColorBrush(color);
                            if (stroke.UseStroke)
                            {
                                square.Stroke = new SolidColorBrush(stroke.StrokeColor);
                                square.StrokeThickness = stroke.StrokeThickness;
                            }
                            Shapes.Add(square);
                            square = new Polygon();
                        }
                    }

                    break;
            }
            foreach (Shape shape in Shapes)
            {
                //LayoutRoot.Children.Add(shape);
                //shape.MouseLeftButtonDown += new MouseButtonEventHandler(Tile_MouseEvent);
                shape.MouseLeftButtonUp += new MouseButtonEventHandler(shape_MouseLeftButtonUp);
                shape.MouseEnter += new MouseEventHandler(shape_MouseEnter);
                shape.MouseLeave += new MouseEventHandler(shape_MouseLeave);
                shape.MouseMove += new MouseEventHandler(shape_MouseMove);
            }
            //return Shapes;

            if (!Elements.ContainsKey(state))
                Elements.Add(state, new List<UIElement>());
            foreach (Shape shape in Shapes)
                Elements[state].Add(shape);
        }

        static String getColor(Color color) {
            return ""+ color.R + separator + color.G + separator + color.B + separator + color.A;
        }

        static String getPoint3d(Point3d point) {
            return "" + point.Position.X + separator + point.Position.Y + separator + point.Height;
        }

        static String getPoint(Point point)
        {
            return "" + point.X + separator + point.Y;
        }

        public static String createPyramidModifiers(Point3d position, Point3d size, Point top, Color color)
        {
            return getPoint3d(position) + separator + getPoint3d(size) + separator + getPoint(top) + separator + getColor(color);
        }

        public void AddPyramid(IViewStateVO state, ItemType type, Point3d position, Point3d size, Point top, Color color, StrokeHelper stroke, bool useMouse)
        {
            List<Point3d> vertices = new List<Point3d>();

            //frente
            vertices.Add(new Point3d(new Point(position.Position.X - size.Position.X / 2, position.Position.Y + size.Position.Y / 2), position.Height));
            vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), position.Height));
            vertices.Add(new Point3d(new Point(top.X, top.Y), position.Height+size.Height));            

            //lado
            vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y + size.Position.Y / 2), position.Height));
            vertices.Add(new Point3d(new Point(position.Position.X + size.Position.X / 2, position.Position.Y - size.Position.Y / 2), position.Height));
            vertices.Add(new Point3d(new Point(top.X, top.Y), position.Height+size.Height));            

            List<Shape> Shapes = new List<Shape>();
            switch (type)
            {
                case ItemType.Normal:
                    /*Point p1 = new Point(vertices[0].Position.X, vertices[0].Position.Y);
                    Point p2 = new Point(vertices[1].Position.X, vertices[1].Position.Y);
                    Point p3 = new Point(vertices[2].Position.X, vertices[2].Position.Y);
                    Point p4 = new Point(vertices[3].Position.X, vertices[3].Position.Y);

                    Polygon t1 = new Polygon();
                    t1.Points.Add(p1);
                    t1.Points.Add(p2);
                    t1.Points.Add(p3);
                    t1.Points.Add(p4);

                    t1.Fill = new SolidColorBrush(color);
                    if (stroke.UseStroke)
                    {
                        t1.Stroke = new SolidColorBrush(stroke.StrokeColor);
                        t1.StrokeThickness = stroke.StrokeThickness;
                    }

                    Shapes.Add(t1);
                    */
                    break;
                case ItemType.Isometric:
                    Polygon triangle = new Polygon();
                    foreach (Point3d point in vertices)
                    {
                        triangle.Points.Add(IsometricMath.PointFrom3d(point));
                        if (triangle.Points.Count == 3)
                        {
                            triangle.Fill = new SolidColorBrush(color);
                            if (stroke.UseStroke)
                            {
                                triangle.Stroke = new SolidColorBrush(stroke.StrokeColor);
                                triangle.StrokeThickness = stroke.StrokeThickness;
                            }
                            Shapes.Add(triangle);
                            triangle = new Polygon();
                        }
                    }

                    break;
            }
            foreach (Shape shape in Shapes)
            {
                //LayoutRoot.Children.Add(shape);
                //shape.MouseLeftButtonDown += new MouseButtonEventHandler(Tile_MouseEvent);
                shape.MouseLeftButtonUp += new MouseButtonEventHandler(shape_MouseLeftButtonUp);
                shape.MouseEnter += new MouseEventHandler(shape_MouseEnter);
                shape.MouseLeave += new MouseEventHandler(shape_MouseLeave);
                shape.MouseMove += new MouseEventHandler(shape_MouseMove);
            }
            //return Shapes;

            if (!Elements.ContainsKey(state))
                Elements.Add(state, new List<UIElement>());
            foreach (Shape shape in Shapes)
                Elements[state].Add(shape);
        }

        public static String createEllipseModifiers(Point3d position, Point size, Color color)
        {
            return getPoint3d(position) + separator + getPoint(size) + separator + getColor(color);
        }

        public void AddEllipse(IViewStateVO state, Point3d position, Point size, Color color, StrokeHelper stroke, bool useMouse)
        {
            Ellipse ellipse = new Ellipse();
            ellipse.Width = size.X;
            ellipse.Height = size.Y;
            ellipse.Fill = new SolidColorBrush(color);
            if(stroke.UseStroke){
                ellipse.Stroke = new SolidColorBrush(stroke.StrokeColor);
                ellipse.StrokeThickness = stroke.StrokeThickness;
            }
            
            Canvas.SetTop(ellipse, -(position.Height + size.Y / 2));
            Canvas.SetLeft(ellipse, - size.X / 2);

            if (useMouse)
            {
                ellipse.MouseLeftButtonUp += new MouseButtonEventHandler(shape_MouseLeftButtonUp);
                ellipse.MouseEnter += new MouseEventHandler(shape_MouseEnter);
                ellipse.MouseLeave += new MouseEventHandler(shape_MouseLeave);
                ellipse.MouseMove += new MouseEventHandler(shape_MouseMove);
            }

            if (!Elements.ContainsKey(state))
                Elements.Add(state, new List<UIElement>());
            Elements[state].Add(ellipse);
            //LayoutRoot.Children.Add(ellipse);
        }

        void shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (ItemMouseLeftButtonUp != null)
                ItemMouseLeftButtonUp(this, e);
        }

        void shape_MouseMove(object sender, MouseEventArgs e)
        {
            if (ItemMouseMove != null)
                ItemMouseMove(this, e);
        }

        void shape_MouseLeave(object sender, MouseEventArgs e)
        {
            if (ItemMouseLeave != null)
                ItemMouseLeave(this, e);
        }

        void shape_MouseEnter(object sender, MouseEventArgs e)
        {
            if (ItemMouseEnter != null)
                ItemMouseEnter(this, e);
        }

        public class StrokeHelper {
            public bool UseStroke { get; set; }
            public Color StrokeColor { get; set; }
            public double StrokeThickness { get; set; }
        }
    }
}